package com.tbs.easyhomemoneyserver.config.filters;

import com.tbs.easyhomemoneyserver.DO.ApiLimit;
import com.tbs.easyhomemoneyserver.config.AppSetting;
import com.tbs.easyhomemoneyserver.constants.CommonConstants;
import com.tbs.easyhomemoneyserver.exception.WarnException;
import com.tbs.easyhomemoneyserver.interfaces.IRightAuthorization;
import com.tbs.easyhomemoneyserver.model.ApiRequest;
import com.tbs.easyhomemoneyserver.model.ApiRightDTO;
import com.tbs.easyhomemoneyserver.service.IApiLimitService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.filter.RequestContextFilter;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;


@WebFilter(urlPatterns = "/api/*")
@Slf4j
public class LoginFiltter extends OncePerRequestFilter {


    @Resource
    IApiLimitService apiLimitService;

    @Resource
    AppSetting appSetting;


    /**
     * 拦截器产生的异常的抛出处理
     *
     * @param e        异常
     * @param request  请求
     * @param response 相应
     * @throws ServletException
     * @throws IOException
     */
    public static void throwError(Throwable e, ServletRequest request, ServletResponse response) throws ServletException, IOException {
        request.setAttribute(CommonConstants.ERROR_ATTR_NAME, e);
        // 指定处理该请求的处理器
        request.getRequestDispatcher(CommonConstants.ERROR_PATH).forward(request, response);
    }

    /**
     * 权限检查
     *
     * @param needRights 所需的权限，String是role 权限名
     * @param request    请求
     * @param apiRequest 拦截到的用户信息
     * @throws ServletException
     */
    private void checkRight(Set<Map.Entry<String, ApiLimit>> needRights, HttpServletRequest request, ApiRequest apiRequest) throws ServletException {
        for (Map.Entry<String, ApiLimit> s : needRights) {
            //获取权限验证类
            IRightAuthorization rightAuthorization = apiLimitService.getAuthMethod(s.getValue());
            if (rightAuthorization == null) {
                log.warn("not found authMethod for {}", s.getValue());
                continue;
            }
            //验证
            if (rightAuthorization.auth(s.getValue(), request, apiRequest)) {
                continue;
            }
            //验证失败抛出异常
            throw new ServletException(rightAuthorization.unAuthedText(apiRequest, s.getValue()));
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        //获取请求路径
        HttpServletRequest servlet = (HttpServletRequest) servletRequest;
        String url = servlet.getRequestURI();
        try {
            ApiRightDTO rights = apiLimitService.getEnabledRights(url);
            //根据http参数传递的loginId参数获取登录信息
            String loginId = servlet.getParameter("loginId");
            log.info("invoked {} by {} ", url, loginId);
            if (!rights.noLoginAccess() && StringUtils.isEmpty(loginId)) {
                throw new ServletException("接口需要登录，空的登录TOKEN");
            }
            ApiRequest apiRequest = null;
            try {
                apiRequest = apiLimitService.getLoginInfo(loginId);
            } catch (WarnException e) {
                throw new ServletException("接口需要登录,无效的登录TOKEN");
            }
            if (apiRequest == null || (!apiRequest.getLogin() && !rights.noLoginAccess())) {
                throw new ServletException("接口需要登录,登录信息无效");
            }
            apiRequest.setRightInfo(rights);
            if (apiRequest.getLogin()) {
                Map<Integer, Set<String>> userRights = apiLimitService.findUserRights(apiRequest.getUserInfo().getId());
                apiRequest.setUserOwnsRights(userRights);
                for (Map.Entry<Integer, Map<String, ApiLimit>> s : rights.getRightsMap().entrySet()) {
                    checkRight(s.getValue().entrySet(), servlet, apiRequest);
                }
            }
        } catch (Throwable throwable) {
            throwError(throwable, servletRequest, servletResponse);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

}
